/***********************************************************************
    filename:   CEGUIFalImageryComponent.cpp
    created:    Mon Jun 13 2005
    author:     Paul D Turner <paul@cegui.org.uk>
*************************************************************************/

#include "renderers/ImageryComponent.h"
#include "hgeImage.h"
#include "hgeExceptions.h"
#include "hgeImageManager.h"
#include "hgePropertyHelper.h"
#include <iostream>

ImageryComponent::ImageryComponent() :
    d_image(0),
    d_vertFormatting(VF_TOP_ALIGNED),
    d_horzFormatting(HF_LEFT_ALIGNED)
{}

const Image* ImageryComponent::getImage() const
{
    return d_image;
}

void ImageryComponent::setImage(const Image* image)
{
    d_image = image;
}

void ImageryComponent::setImage(const String& image)
{
    try
    {
        d_image = &ImageManager::getSingleton().getImage(image);
		d_area = hgeRect(0, d_image->getWidth(), 0, d_image->getHeight());
    }
    catch (UnknownObjectException&)
    {
        d_image = 0;
    }
}

VerticalFormatting ImageryComponent::getVerticalFormatting() const
{
    return d_vertFormatting;
}

void ImageryComponent::setVerticalFormatting(VerticalFormatting fmt)
{
    d_vertFormatting = fmt;
}

HorizontalFormatting ImageryComponent::getHorizontalFormatting() const
{
    return d_horzFormatting;
}

void ImageryComponent::setHorizontalFormatting(HorizontalFormatting fmt)
{
    d_horzFormatting = fmt;
}

void ImageryComponent::render_impl(Window& srcWindow, hgeRect& destRect, const ColourRect* modColours, const hgeRect* clipper, bool /*clipToDisplay*/) const
{
    // get final image to use.
    const Image* img = isImageFetchedFromProperty() ?
        PropertyHelper::stringToImage(srcWindow.getProperty(d_imagePropertyName)) :
        d_image;

    // do not draw anything if image is not set.
    if (!img)
        return;

    HorizontalFormatting horzFormatting = d_horzFormatPropertyName.empty() ? d_horzFormatting :
        PropertyHelper::stringToHorzFormat(srcWindow.getProperty(d_horzFormatPropertyName));

    VerticalFormatting vertFormatting = d_vertFormatPropertyName.empty() ? d_vertFormatting :
        PropertyHelper::stringToVertFormat(srcWindow.getProperty(d_vertFormatPropertyName));

    UINT horzTiles, vertTiles;
    float xpos, ypos;

    Size imgSz(img->getSize());

    // calculate final colours to be used
    ColourRect finalColours;
    initColoursRect(srcWindow, modColours, finalColours);

    // calculate initial x co-ordinate and horizontal tile count according to formatting options
    switch (horzFormatting)
    {
        case HF_STRETCHED:
            imgSz.d_x = destRect.getWidth();
            xpos = destRect.d_left;
            horzTiles = 1;
            break;

        case HF_TILED:
            xpos = destRect.d_left;
            horzTiles = (UINT)((destRect.getWidth() + (imgSz.d_x - 1)) / imgSz.d_x);
            break;

        case HF_LEFT_ALIGNED:
            xpos = destRect.d_left;
            horzTiles = 1;
            break;

        case HF_CENTRE_ALIGNED:
            xpos = destRect.d_left + PixelAligned((destRect.getWidth() - imgSz.d_x) * 0.5f);
            horzTiles = 1;
            break;

        case HF_RIGHT_ALIGNED:
            xpos = destRect.d_right - imgSz.d_x;
            horzTiles = 1;
            break;

        default:
            throw InvalidRequestException("ImageryComponent::render - An unknown HorizontalFormatting value was specified.");
    }

    // calculate initial y co-ordinate and vertical tile count according to formatting options
    switch (vertFormatting)
    {
        case VF_STRETCHED:
            imgSz.d_y = destRect.getHeight();
            ypos = destRect.d_top;
            vertTiles = 1;
            break;

        case VF_TILED:
            ypos = destRect.d_top;
            vertTiles = (UINT)((destRect.getHeight() + (imgSz.d_y - 1)) / imgSz.d_y);
            break;

        case VF_TOP_ALIGNED:
            ypos = destRect.d_top;
            vertTiles = 1;
            break;

        case VF_CENTRE_ALIGNED:
            ypos = destRect.d_top + PixelAligned((destRect.getHeight() - imgSz.d_y) * 0.5f);
            vertTiles = 1;
            break;

        case VF_BOTTOM_ALIGNED:
            ypos = destRect.d_bottom - imgSz.d_y;
            vertTiles = 1;
            break;

        default:
            throw InvalidRequestException("ImageryComponent::render - An unknown VerticalFormatting value was specified.");
    }

    // perform final rendering (actually is now a caching of the images which will be drawn)
    hgeRect finalRect;
    hgeRect finalClipper;
    const hgeRect* clippingRect;
    finalRect.d_top = ypos;
    finalRect.d_bottom = ypos + imgSz.d_y;

    for (UINT row = 0; row < vertTiles; ++row)
    {
        finalRect.d_left = xpos;
        finalRect.d_right = xpos + imgSz.d_x;

        for (UINT col = 0; col < horzTiles; ++col)
        {
            // use custom clipping for right and bottom edges when tiling the imagery
            if (((vertFormatting == VF_TILED) && row == vertTiles - 1) ||
                ((horzFormatting == HF_TILED) && col == horzTiles - 1))
            {
                finalClipper = clipper ? clipper->getIntersection(destRect) : destRect;
                clippingRect = &finalClipper;
            }
            // not tiliing, or not on far edges, just used passed in clipper (if any).
            else
            {
                clippingRect = clipper;
            }

            // add geometry for image to the target window.
            img->draw(srcWindow.getGeometryBuffer(), finalRect, clippingRect, finalColours);

            finalRect.d_left += imgSz.d_x;
            finalRect.d_right += imgSz.d_x;
        }

        finalRect.d_top += imgSz.d_y;
        finalRect.d_bottom += imgSz.d_y;
    }
}

bool ImageryComponent::isImageFetchedFromProperty() const
{
    return !d_imagePropertyName.empty();
}

const String& ImageryComponent::getImagePropertySource() const
{
    return d_imagePropertyName;
}

void ImageryComponent::setImagePropertySource(const String& property)
{
    d_imagePropertyName = property;
}

